#include "../process.h"
#include "../util.h"
#include "../config.h"

#define BATCH 20000

inline bool is_run(int year)
{
	return (!year%100)?(!year%400):(!year%4);
}

inline bool year2sec(int year)
{
	int res = 0;
	for(int i = 0; i < year; ++ i)
	{
		if(is_run)
			res += 31622400;
		else
			res += 31536000;
	}
	return res;
}

inline int month2sec(int month, int is_run)
{
	int res = 0;
	for(int i = 1; i < month; ++ i)
	{
		if(month == 1 || month == 3|| month == 5|| month == 7|| month == 8|| month == 10|| month == 12)
			res += 2678400;
		else if(month == 4|| month == 6|| month == 9|| month == 11)
			res += 2592000;
		else if(month == 2)
			res += is_run?2505600:2822400;
	}
	return res;
}

inline int day2sec(int date)
{
	return (date - 1)*216000;
}

inline int hour2sec(int hour)
{
	return 3600*hour;
}

inline int min2sec(int min)
{
	return 60*min;
}

inline int parse_time(string time_s)
{
	vector<string> time_v = util::tokenize(time_s, ": -");
	int year = atoi(time_v[0].c_str());
	int month = atoi(time_v[1].c_str());
	int day = atoi(time_v[2].c_str());
	int hour = atoi(time_v[3].c_str());
	int min = atoi(time_v[4].c_str());
	int sec = atoi(time_v[5].c_str());
	return year2sec(year) + month2sec(month, is_run(year)) + day2sec(day) + hour2sec(hour) + min2sec(min) + sec;
}

vector<set<int>> graph;
map<string, int> dictionary;
vector<string> queries;
		
inline void display(iostream *out, vector<set<int>> graph)
{
	for(int i = 0; i < graph.size(); ++ i)
	{
		set<int> &e_s = graph[i];
		if(e_s.size() > 0){
			*out << queries[i] << ":";	
			set<int>::iterator itr_e;
			for(itr_e = e_s.begin(); itr_e != e_s.end(); ++ itr_e)
				*out << queries[*itr_e] << " | ";
			*out << endl;
		}
	}
}

int main(int argc, char *argv[])
{
	fstream input(argv[1], fstream::in);
	fstream output(argv[2], fstream::out);
	fstream config(argv[3], fstream::in);
	fstream log("log.txt", fstream::out);

	Config::load(&config);

	string prev_user_id, cur_user_id;
	int count = 0;
	bool first_line = true;
	vector<pair<int, int>> collection;
	while(!input.eof())
	{
		string line;
		getline(input, line);
		if(line.length() == 0) continue;
		if(count++%BATCH == 0)cout << ".";
		vector<string> v = util::tokenize(line, "\t");
		cur_user_id = v[0];
		if(first_line)
		{
			prev_user_id = cur_user_id;
			first_line = false;
		}
		if(cur_user_id != prev_user_id)
		{
			log << prev_user_id << endl;
			log.flush();
			for(int i = 0; i < collection.size(); ++ i)
			{
				int q_i = collection[i].first;
				int t_i = collection[i].second;
				for(int j = i + 1; j <= i + atoi(Config::get("Query_count").c_str()); ++ j)
				{
					int q_j = collection[j].first;
					int t_j = collection[j].second;
					if(t_j - t_i < atoi(Config::get("Session_Length").c_str()) && t_j - t_i >= 0 && q_i != q_j)
					{
						graph[q_i].insert(q_j);
						graph[q_j].insert(q_i);
					}
					else{
						i = j;
						break;
					}
				}
			}
			collection.clear();
			prev_user_id = cur_user_id;
		}
		string query = v[1];
		int q_time = parse_time(v[2]);
		map<string, int>::iterator itr = dictionary.find(query);
		if(itr == dictionary.end()){
			dictionary.insert(pair<string, int>(query, queries.size()));
			collection.push_back(pair<int, int>(queries.size(), q_time));
			graph.push_back(set<int>());
			queries.push_back(query);
		}
		else
			collection.push_back(pair<int, int>((*itr).second, q_time));
		
		
	}
	for(int i = 0; i < collection.size(); ++ i)
	{
		int q_i = collection[i].first;
		int t_i = collection[i].second;
		for(int j = i + 1; j <= i + atoi(Config::get("Query_count").c_str()); ++ j)
		{
			int q_j = collection[j].first;
			int t_j = collection[j].second;
			if(t_j - t_i < atoi(Config::get("Session_Length").c_str()) && t_j - t_i >= 0 && q_i != q_j)
			{
				graph[q_i].insert(q_j);
				graph[q_j].insert(q_i);
			}
			else{
				i = j;
				break;
			}
		}
	}

	display(&output, graph);
	input.close();
	output.close();
	log.close();
	return 0;
}